package com.sudo.api.interceptor;

import com.sudo.common.annotation.ApiRateLimiter;
import com.sudo.common.component.RedisKeys;
import com.sudo.common.component.StringRedisUtil;
import com.sudo.common.exception.BizException;
import com.sudo.common.utils.HttpUserInfoUtil;
import com.sudo.common.utils.IpAddressUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @Author：DayDayUp
 * @Create：2023/2/6 15:34
 */
@Order(1)
@Slf4j
@Component
public class ApiRateLimiterInterceptor implements HandlerInterceptor {

    @Resource
    private StringRedisUtil stringRedisUtil;

    @Resource
    private RedisKeys redisKeys;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        // handler是否为 HandleMethod 实例
        if (handler instanceof HandlerMethod) {
            // 强转
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            String clazzName = handlerMethod.getBeanType().getName();
            // 获取方法
            Method method = handlerMethod.getMethod();

            String username = HttpUserInfoUtil.getSessionUser(request);
            String ip = IpAddressUtil.getIpAddress(request);
            String userAgent = request.getHeader("user-agent");
            if (StringUtils.isBlank(ip) || StringUtils.isBlank(userAgent)){
                throw new BizException("当前请求错误");
            }

            // ip + user    5s 20次访问
            String userWebKey;
            // ip + user + class + method
            String userApiKey;
            if (StringUtils.isNotBlank(username)){
                userWebKey = redisKeys.userWebLimit(username);
                userApiKey = redisKeys.userApiLimit(username, clazzName, method.getName());
            }else {
                userWebKey = redisKeys.userWebLimit(ip);
                userApiKey = redisKeys.userApiLimit(ip, clazzName, method.getName());
            }


            int maxAccessCnt;
            long maxAccessTime;
            // 判断方式是否有AccessLimit注解，有的才需要做限流
            if (!method.isAnnotationPresent(ApiRateLimiter.class)) {
                maxAccessCnt = 2;
                maxAccessTime = 1L;
            }else {
                // 获取注解上的内容
                ApiRateLimiter apiRateLimiter = method.getAnnotation(ApiRateLimiter.class);
                maxAccessCnt = apiRateLimiter.times();
                maxAccessTime = apiRateLimiter.second();
            }

            String userWebValue = stringRedisUtil.get(userWebKey);
            if (Objects.isNull(userWebValue)){
                stringRedisUtil.set(userWebKey,"1",5);
            }else {
                int currentCnt = Integer.parseInt(userWebValue) + 1;
                if (currentCnt <= 20){
                    long expire = stringRedisUtil.getExpire(userWebKey);
                    if (expire > 0){
                        stringRedisUtil.set(userWebKey,String.valueOf(currentCnt),expire);
                    }
                }else {
                    throw new BizException("访问次数已达上线，请稍后再试！");
                }
            }


            String userApiValue = stringRedisUtil.get(userApiKey);
            if (Objects.isNull(userApiValue)){
                stringRedisUtil.set(userApiKey,"1",maxAccessTime);
            }else{
                int currentCnt = Integer.parseInt(userApiValue) + 1;
                if (currentCnt <= maxAccessCnt){
                    long expire = stringRedisUtil.getExpire(userApiKey);
                    if (expire > 0){
                        stringRedisUtil.set(userApiKey,String.valueOf(currentCnt),expire);
                    }
                }else {
                    throw new BizException("访问次数已达上线，请稍后再试！");
                }
            }
        }
        return true;
    }
}
